#include "../inc/TicTacToe.h"
#include "../inc/swipedetector.h"
#include <iostream>
#include <algorithm>

/*
3*3图片打印位置
第一个 0,30、 第二个 135,30、 第三个 270,30
第四个 0,175、第五个 135,175、第六个 270,175
第七个 0,320、第八个 135,320、第九个 270,320
*/

//创建tictactoe的BMP对象
BMP XWin("./tictactoe/XWin.bmp");
BMP OWin("./tictactoe/OWin.bmp");
BMP GameOver("./tictactoe/GameOver.bmp");
BMP xoxocontinue("./tictactoe/continue.bmp");
BMP xoxopuase("./tictactoe/puase.bmp");
BMP tictactoeface("./tictactoe/tictactoeface.bmp");
BMP tictactoe_chess("./tictactoe/tictactoe_chess.bmp");
BMP tictactoe_circle("./tictactoe/tictactoe_circle.bmp");
BMP tictactoe_cross("./tictactoe/tictactoe_cross.bmp");
BMP tictactoe_null("./tictactoe/tictactoe_null.bmp");
BMP tictactoe_right("./tictactoe/tictactoe_right.bmp");

/**
 * @brief TicTacToe类的构造函数
 *
 * 初始化棋盘和游戏状态
 */
TicTacToe::TicTacToe() 
{
    restartGame();  // 使用restartGame来初始化游戏
}

/**
 * @brief 重启游戏
 *
 * 此函数用于重启井字游戏。它会重置棋盘、设置先手玩家，并标记游戏未结束。
 */
void TicTacToe::restartGame()
{
    board = {'1', '2', '3', '4', '5', '6', '7', '8', '9'}; // 初始化棋盘
    currentPlayer = 'X';                                   // X先手
    gameOver = false;                                      // 游戏未结束
}

/**
 * @brief 井字棋游戏的主函数
 *
 * 该函数实现了一个简单的井字棋游戏逻辑。游戏通过SwipeDetector类检测玩家的滑动输入，并根据玩家的滑动位置来在井字棋盘上落子。
 * 游戏支持两位玩家交替进行，分别使用'X'和'O'作为棋子。游戏会在棋盘填满或某一玩家连成一线时结束，并宣布胜利者或平局。
 */
void TicTacToe::playGame(std::string username, bool userLoginFlag) 
{
    int position;// 玩家选择的棋盘位置
    bool musicPlayFlag = true; // 音乐播放标志位，默认为false
    char text[50];// 文本缓冲区，用于显示用户名
    int Xwin = 0; // X胜利标志
    int Owin = 0; // O胜利标志
    int Draw = 0; // 平局标志

    ReturnInterfaceFlag tic_flag;//声明ReturnInterfaceFlag类

    tic_flag.userLoginFlag = userLoginFlag;     //保存用户登录标志
    tic_flag.UserName=username;                 //保存用户名
    cout<<"用户名："<<tic_flag.UserName<<endl;  //打印用户名

    const int positions[9][4] = 
    { // 每个格子的坐标范围 [x1, x2, y1, y2]
    {0, 135, 30, 175},    // 位置1
    {135, 270, 30, 175},  // 位置2
    {270, 405, 30, 175},  // 位置3
    {0, 135, 175, 320},   // 位置4
    {135, 270, 175, 320}, // 位置5
    {270, 405, 175, 320}, // 位置6
    {0, 135, 320, 465},   // 位置7
    {135, 270, 320, 465}, // 位置8
    {270, 405, 320, 465}  // 位置9
    };

    SwipeDetector tic_detector;//创建滑动检测器，获取坐标信息
    SwipeDetector::Direction tic_direction;//滑动方向枚举类型变量，用于存储用户滑动的方向

    ShowFont* tic_showFont = ShowFont::instance();//创建ShowFont对象,打印文字

    MusicPlayer tic_player;//创建音乐播放器实例，用于播放背景音乐和得分音效

    std::cout << "玩家1: X, 玩家2: O\n";

    printBoard();// 打印当前棋盘状态

    tictactoeface.show(Lcd::instance()->addr(), 0, 0);// 显示井字棋的背景图片
    tic_player.play("./tictactoe/tictactoe.mp3");//播放背景音乐

    // 判断是否是用户登录
    if (tic_flag.userLoginFlag == true)
    {
        // 显示用户名
        sprintf(text, "%s", tic_flag.UserName.c_str());//flag.UserName为用户名
        tic_showFont->display(text, 32, 100, 32, ShowFont::purple, ShowFont::white, 590, 15);
    }

    TimeDisplay tic_timeDisplay;//创建时间类实例，用于显示时间
    tic_timeDisplay.start();//打印时间

    // 游戏主循环
    while (1) 
    {
        if (tic_player.isFinished()) 
        {
        std::cout << "音乐播放结束，重新播放" << std::endl;
        tic_player.play("./tictactoe/tictactoe.mp3");
        }

        tic_detector.detectSwipe();// 检测滑动方向
        cout << "玩家 " << currentPlayer << ", 请输入位置(1-9): "<<endl;

        if (!gameOver)
        {
            for (int i = 0; i < 9; ++i) 
            {
                if (tic_detector.getX() >= positions[i][0] && tic_detector.getX() < positions[i][1] &&
                    tic_detector.getY() >= positions[i][2] && tic_detector.getY() < positions[i][3]) 
                {
                    position = i + 1; // 转换为1-9的位置编号
                    break;
                }
            }
        }
        else // 如果游戏已经结束，则打印棋盘并显示结果
        {
            // 检查从新开始的按钮的检测逻辑
            if (tic_detector.getX() >= 0 && tic_detector.getX() < 480 &&
                    tic_detector.getY() >= 0 && tic_detector.getY() < 480) 
            {
                restartGame();
                gameOver = false;
                position = 0;
                //清空坐标
                tic_detector.setXY(0, 0);
                printBoard();// 打印当前棋盘状态
                std::cout << "重新开始游戏" << std::endl;
            }
        }

        // 退出按钮的检测逻辑
        if (tic_detector.getX() >= 700 && tic_detector.getX() < 800 &&
            tic_detector.getY() >= 0 && tic_detector.getY() < 60) 
        {
            tic_timeDisplay.stop();// 停止计时器
            tic_player.stop();// 停止播放音乐
            return;
        }
        // 音乐暂停和继续按钮的检测逻辑
        else if (tic_detector.getX() >= 715 && tic_detector.getX() < 800 &&
                 tic_detector.getY() >= 380 && tic_detector.getY() < 480) 
        {
            if (musicPlayFlag == true)
            {
                tic_player.pause();// 暂停
                std::cout << "Paused playback" << std::endl;
                musicPlayFlag = false;
            }
            else
            {
                tic_player.resume();// 继续播放
                std::cout << "Resumed playback" << std::endl;
                musicPlayFlag = true;
            }
        }
            
        if (makeMove(position)) // 尝试在指定位置落子，并检查游戏状态
        {
            printBoard();// 打印当前棋盘状态 
            if (checkWin()) // 检查是否有玩家获胜或游戏是否结束
            {
                //如果玩家X赢了
                if (currentPlayer == 'X')
                {
                    // 显示玩家X胜利的图片
                    XWin.show(Lcd::instance()->addr(), 0, 20);
                    // 清空文本缓冲区，防止重复打印分数
                    memset(text, 0, sizeof(text));
                    // 打印玩家X胜利的分数
                    sprintf(text, "%d", ++Xwin);
                    tic_showFont->display(text, 32, 50, 32, ShowFont::purple, ShowFont::white, 480, 265);
                }
                else // 如果玩家O赢了
                {
                    // 显示玩家O胜利的图片
                    OWin.show(Lcd::instance()->addr(), 0, 20);
                    // // 清空文本缓冲区，防止重复打印分数
                    memset(text, 0, sizeof(text));
                    // // 打印玩家O胜利的分数
                    sprintf(text, "%d", ++Owin);
                    tic_showFont->display(text, 32, 50, 32, ShowFont::purple, ShowFont::white, 710, 265);
                }
                std::cout << "玩家 " << currentPlayer << " 获胜!\n";
                gameOver = true;
            }
            else if (checkDraw()) // 检查是否为平局
            {
                // 显示平局图片
                GameOver.show(Lcd::instance()->addr(), 0, 20);
                // // 清空文本缓冲区，防止重复打印分数
                memset(text, 0, sizeof(text));
                // // 打印平局分数
                sprintf(text, "%d", ++Draw);
                tic_showFont->display(text, 32, 50, 32, ShowFont::purple, ShowFont::white, 610, 265);
                std::cout << "平局!\n";
                gameOver = true;
            }
            else 
            {
                switchPlayer();// 切换当前玩家
            }
        }
        else 
        {
            std::cout << "无效落子，请重试!\n";
        }
    }
}

/**
 * @brief 打印井字游戏棋盘
 *
 * 该函数负责打印井字游戏的棋盘状态。棋盘是一个3x3的网格，每个格子可以是'X', 'O'或空格。
 * 打印坐标从1到9，对应棋盘上的位置。
 * 第一个 0,30、 第二个 135,30、 第三个 270,30
 * 第四个 0,175、第五个 135,175、第六个 270,175
 * 第七个 0,320、第八个 135,320、第九个 270,320
 * X: 井字棋的X图标:tictactoe_cross.bmp
 * O: 井字棋的O图标:tictactoe_circle.bmp
 * 空格: 井字棋的空图标:tictactoe_null.bmp
 * 
 * @note 该函数是一个常量成员函数，不会修改对象的状态。
 */
void TicTacToe::printBoard() const 
{
    // 显示井字棋的棋盘背景图片
    tictactoe_chess.show(Lcd::instance()->addr(), 0, 0);
    // 定义每个格子的坐标位置
    const int positions[9][2] = 
    {
        {0, 30},    // 位置1
        {135, 30},  // 位置2
        {270, 30},  // 位置3
        {0, 175},   // 位置4
        {135, 175}, // 位置5
        {270, 175}, // 位置6
        {0, 320},   // 位置7
        {135, 320}, // 位置8
        {270, 320}  // 位置9
    };

    // 遍历棋盘上的每个位置
    for (int i = 0; i < 9; ++i) 
    {
        // 根据棋盘状态显示对应的图片
        switch (board[i]) 
        {
            case 'X':
                tictactoe_cross.show(Lcd::instance()->addr(), positions[i][0], positions[i][1]);
                break;
            case 'O':
                tictactoe_circle.show(Lcd::instance()->addr(), positions[i][0], positions[i][1]);
                break;
            default:  // 空格
                tictactoe_null.show(Lcd::instance()->addr(), positions[i][0], positions[i][1]);
                break;
        }
    }
}

bool TicTacToe::makeMove(int position) 
{
    if (!isMoveValid(position)) 
    {
        return false;
    }
    
    board[position - 1] = currentPlayer; // 数组索引从0开始
    return true;
}

bool TicTacToe::checkWin() const 
{
    // 检查所有可能的胜利组合
    const int winCombinations[8][3] = 
    {
        {0, 1, 2}, {3, 4, 5}, {6, 7, 8}, // 横线
        {0, 3, 6}, {1, 4, 7}, {2, 5, 8}, // 竖线
        {0, 4, 8}, {2, 4, 6}             // 对角线
    };
    
    for (const auto &combo : winCombinations) 
    {
        if (board[combo[0]] == board[combo[1]] && 
            board[combo[1]] == board[combo[2]]) 
        {
            return true;
        }
    }
    return false;
}

bool TicTacToe::checkDraw() const 
{
    // 检查是否所有位置都被占据
    return std::all_of(board.begin(), board.end(), [](char c) 
    {
        return c == 'X' || c == 'O';
    });
}

void TicTacToe::switchPlayer() 
{
    currentPlayer = (currentPlayer == 'X') ? 'O' : 'X';
}

bool TicTacToe::isMoveValid(int position) const 
{
    // 检查位置是否在1-9范围内且未被占据
    return position >= 1 && position <= 9 && 
           board[position - 1] != 'X' && 
           board[position - 1] != 'O';
}

